
/*****************************************************************************\
 *                             ColGeometry
\*****************************************************************************/

/*! @file
 *
 *  @brief
 *    The geometry data structure used in CollDet .
 *
 *  @pre
 *
 *  @author Weiyu Yi, wyi@tu-clausthal.de, written in 2010.
 *
 *  @warning
 *    The external access and the saving of data use the single precision of
 *    floating number, while the internal calculation, ie. constructing, uses
 *    double precision.
 *
 *  @todo
 *	  re-organizing it in order to take deformable geometry
 *	  re-organizing it in order to render fast
 *
 */

//---------------------------------------------------------------------------
//  Includes
//---------------------------------------------------------------------------

#include <cstdio>

#define COL_EXPORT

#include <col_import_export.h>
#include "ColGeometry.h"

namespace col {

//---------------------------------------------------------------------------
//  Forward References
//---------------------------------------------------------------------------


//***************************************************************************
//  ColGeometry 
//***************************************************************************


/** default constructor
 *
 * the geometry is usually assigned by some other objects 
 */
ColGeometry::ColGeometry()
:
	m_name( NULL ),
	m_pt( ANY )
{
	m_bboxmin[0] =  FLT_MAX;
	m_bboxmin[1] =  FLT_MAX;
	m_bboxmin[2] =  FLT_MAX;
	
	m_bboxmax[0] = -FLT_MAX;
	m_bboxmax[1] = -FLT_MAX;
	m_bboxmax[2] = -FLT_MAX;
}

ColGeometry::ColGeometry( const Pnt3Array &points, const std::vector<Primitive> &prims, PrimitiveTypeE pt )
	:
	m_name( NULL ),
	m_pt( pt ),
	m_points( points ),
	m_primitives( prims )
{
	m_bboxmin[0] =  FLT_MAX;
	m_bboxmin[1] =  FLT_MAX;
	m_bboxmin[2] =  FLT_MAX;
	
	m_bboxmax[0] = -FLT_MAX;
	m_bboxmax[1] = -FLT_MAX;
	m_bboxmax[2] = -FLT_MAX;

	updateBBox();
}

/** copy constructor
 */
ColGeometry::ColGeometry( const ColGeometry &source )
	:
	m_name(source.m_name),
	m_pt(source.m_pt),
	m_points(source.m_points),
	m_primitives(source.m_primitives)
{
	m_bboxmin[0] = source.m_bboxmin[0];
	m_bboxmin[1] = source.m_bboxmin[1];
	m_bboxmin[2] = source.m_bboxmin[2];
	
	m_bboxmax[0] = source.m_bboxmax[0];
	m_bboxmax[1] = source.m_bboxmax[1];
	m_bboxmax[2] = source.m_bboxmax[2];
}

/** assignment operator
 */
const ColGeometry & ColGeometry::operator=( const ColGeometry &source )
{
	m_name = source.m_name;
	m_pt = source.m_pt;

	// copy the points
	m_points = source.m_points;

	// copy the primitives
	m_primitives = source.m_primitives;

	// copy the bbox
	m_bboxmin[0] = source.m_bboxmin[0];
	m_bboxmin[1] = source.m_bboxmin[1];
	m_bboxmin[2] = source.m_bboxmin[2];
	
	m_bboxmax[0] = source.m_bboxmax[0];
	m_bboxmax[1] = source.m_bboxmax[1];
	m_bboxmax[2] = source.m_bboxmax[2];

	return *this;
}

/** get the pointer to the points of this geometry
 *
 * @brief through this function the points of the geometry could be changed by caller
 */
Pnt3Array* ColGeometry::editPointsPtr() { return &m_points; }
	
/** get the pointer to all the primitives of this geometry
 *
 * @brief through this function the primitives of the geometry could be changed by caller
 */
std::vector<Primitive>* ColGeometry::editPrimitivesPtr() { return &m_primitives; }

/** get the pointer to the points of this geometry
 *
 * @brief the returned pointer is const, cannot be used to change the geometry
 */
const Pnt3Array* ColGeometry::getPointsPtr() const { return &m_points; }

/** get the pointer to the primitives of this geometry
 *
 * @brief the returned pointer is const, cannot be used to change the geometry
 */	
const std::vector<Primitive>* ColGeometry::getPrimitivesPtr() const { return &m_primitives; }

/** get the reference of the points of this geometry
 *
 * @brief the returned reference is const, cannot be used to change the geometry
 */
const Pnt3Array& ColGeometry::getPoints() const 
{ 
	const Pnt3Array &rpoints = m_points;
	return rpoints;
}

/** get the reference of the points of this geometry
 *
 * @brief the returned reference is const, cannot be used to change the geometry
 */
const std::vector<Primitive>& ColGeometry::getPrimitives() const 
{ 
	const std::vector<Primitive> &rprims = m_primitives; 
	return rprims;
}

/** get the primitive type of this geometry
 */
const PrimitiveTypeE ColGeometry::getPrimType() const
{
	return m_pt;
}

/** set the primitive type of this geometry
 */
void ColGeometry::setPrimType ( const PrimitiveTypeE pt )
{
	m_pt = pt;
}
	
/** get the normal type of the geometry
 */
const NormalTypeE ColGeometry::getNormalType( void ) const
{
	return m_nt;
}

/** set the normal type of the geometry
 */
void ColGeometry::setNormalType( const NormalTypeE nt )
{
	m_nt = nt;
}
	
/** get the AABB
 * 
 * @param min the xyz-axies min point
 * @param max the xyz-axies max point
 */
void ColGeometry::getBBox( Point3 &min, Point3 &max ) const
{
	min[0] = m_bboxmin[0];
	min[1] = m_bboxmin[1];
	min[2] = m_bboxmin[2];
	max[0] = m_bboxmax[0];
	max[1] = m_bboxmax[1];
	max[2] = m_bboxmax[2];
}

/** get the AABB
 * 
 * @param min the xyz-axies min point
 * @param max the xyz-axies max point
 */
void ColGeometry::getBBox( float min[], float max[] ) const
{
	min[0] = m_bboxmin[0];
	min[1] = m_bboxmin[1];
	min[2] = m_bboxmin[2];
	max[0] = m_bboxmax[0];
	max[1] = m_bboxmax[1];
	max[2] = m_bboxmax[2];
}
void ColGeometry::getBBox( double min[], double max[] ) const
{
	min[0] = m_bboxmin[0];
	min[1] = m_bboxmin[1];
	min[2] = m_bboxmin[2];
	max[0] = m_bboxmax[0];
	max[1] = m_bboxmax[1];
	max[2] = m_bboxmax[2];
}

/** clear the content
 */
void ColGeometry::clearOldData( void )
{
	m_points.clear();
	m_primitives.clear();
}

/** give this geometry object the content from arrays
 * 
 * @param prim_size the type of the primitive in the geometry
 * @param points REAL[], the array stores the vertices
 * @param np the number of primitives
 * @param indices unsigned int[], the array stores the indices of all primitives
 * @param ni the length of each primitive
 */
void ColGeometry::importDataFromArray( const unsigned int prim_size,
									   const REAL *points, 
									   const unsigned int np,
									   const unsigned int *indices,
									   const unsigned int ni )
{
	clearOldData();

	if ( prim_size == 3 )
		m_pt = TRIANGLES;
	else if ( prim_size == 4 )
		m_pt = QUADS;
	else 
		m_pt = ANY;

	// valid data in points has size: 3 * np
	for ( unsigned int i=0; i<np; i++ )
	{
		m_points.push_back( Point3( points[i*3+0], points[i*3+1], points[i*3+2] ) );
	}

	// valid data in indices has size prim_size * ni
	for ( unsigned int i=0; i<ni; i++ )
	{
		Primitive prim;
		for ( unsigned int j=0; j<prim_size; j++ )
			prim.push_back( indices[ i * prim_size + j ] );
		m_primitives.push_back( prim );
	}

	updateBBox();
}

/** give this geometry object the content from arrays
 * 
 * @param prim_size the type of the primitive in the geometry
 * @param points REAL[], the array stores the vertices
 * @param np the number of primitives
 * @param indices unsigned int[], the array stores the indices of all primitives
 * @param ni the length of each primitive
 * @param normals REAL[], the array stores all the normals
 * @param nt the type of the normals per_vertice or per_primitive
 */
void ColGeometry::importDataFromArray( const unsigned int prim_size,
									   const REAL *points, 
									   const unsigned int np,
									   const unsigned int *indices,
									   const unsigned int ni,
									   const REAL *normals,
									   const NormalTypeE nt )
{
	clearOldData();

	if ( prim_size == 3 )
		m_pt = TRIANGLES;
	else if ( prim_size == 4 )
		m_pt = QUADS;
	else 
		m_pt = ANY;

	// valid data in points has size: 3 * np
	for ( unsigned int i=0; i<np; i++ )
	{
		m_points.push_back( Point3( points[i*3+0], points[i*3+1], points[i*3+2] ) );
	}

	// valid data in indices has size prim_size * ni
	for ( unsigned int i=0; i<ni; i++ )
	{
		Primitive prim;
		for ( unsigned int j=0; j<prim_size; j++ )
			prim.push_back( indices[ i * prim_size + j ] );
		m_primitives.push_back( prim );
	}

	if ( nt == PER_VERTEX )
	{
		m_nt = nt;
		for ( unsigned int i=0; i<np; i++ )
		{
			m_normals.push_back( Vector3( normals[i*3+0], normals[i*3+1], normals[i*3+2] ) );
		}
	}
	else if ( nt == PER_PRIMITIVE )
	{
		m_nt = nt;
		for ( unsigned int i=0; i<ni; i++ )
		{
			m_normals.push_back( Vector3( normals[i*3+0], normals[i*3+1], normals[i*3+2] ) );
		}
	}
	else
	{
		fprintf(stderr, "Errer while importing data from array, no such normal type\n");
	}

	updateBBox();
}
	
/** re-calculate the AABB
 */
void ColGeometry::updateBBox( void )
{
	// assert m_points
	
	// update
	for ( unsigned int i=0; i<m_points.size(); ++i )
	{
		for ( unsigned int j=0; j<3; ++j )
		{
			if ( m_points[i][j] < m_bboxmin[j] )
			{
				m_bboxmin[j] = m_points[i][j];
			}
			if ( m_points[i][j] > m_bboxmax[j] )
			{
				m_bboxmax[j] = m_points[i][j];
			}
		}
	}
	// pos. condition:  m_bboxmax >= m_bboxmin
}

/** print out the content in the geometry
 * 
 * @param fp FILE*, default = stdout
 */
void ColGeometry::print( FILE *fp ) const
{
	if ( m_name != NULL )
		fprintf( fp, "# %s\n", m_name );

	for ( unsigned int i=0; i<m_points.size(); i++ )
	{
		REAL p0 = m_points[i][0];
		REAL p1 = m_points[i][1];
		REAL p2 = m_points[i][2];
		fprintf( fp, "v %f %f %f\n", p0, p1, p2 );
	}
	fprintf( fp, "# %d vertices\n\n", m_points.size() );

	std::vector<Primitive>::const_iterator iter_prim = m_primitives.begin();
	for ( ; iter_prim != m_primitives.end(); ++iter_prim )
	{
		fprintf( fp, "f" );
		for( unsigned int k=0; k < iter_prim->size(); ++k )
		{
			fprintf( fp, " %d",  (*iter_prim)[k] );
		}
		fprintf( fp, "\n" );
	}
	fprintf( fp, "# %d faces\n", m_primitives.size() );

	fprintf( fp, "# BBox: min[%f, %f, %f], max[%f, %f, %f]\n", 
				m_bboxmin[0], m_bboxmin[1], m_bboxmin[2],
				m_bboxmax[0], m_bboxmax[1], m_bboxmax[2] );
}

/** print out the geometry roughly as .obj format
 * 
 * @param file_name const char*
 * 
 * @return 0 if no errors
 */
int ColGeometry::printToObj( const char *file_name ) const
{
	FILE *fp;
	if ( file_name == NULL ) 
		fp = stdout;
	{
		fp = fopen( file_name, "w" );
		if ( fp == NULL ) 
			return -1;
	}

	if ( m_name != NULL )
		fprintf( fp, "# %s\n", m_name );

	for ( unsigned int i=0; i<m_points.size(); i++ )
	{
		REAL p0 = m_points[i][0];
		REAL p1 = m_points[i][1];
		REAL p2 = m_points[i][2];
		fprintf( fp, "v %f %f %f\n", p0, p1, p2 );
	}
	fprintf( fp, "# %d vertices\n\n", m_points.size() );

	std::vector<Primitive>::const_iterator iter_prim = m_primitives.begin();
	for ( ; iter_prim != m_primitives.end(); ++iter_prim )
	{
		fprintf( fp, "f" );
		for( unsigned int k=0; k < iter_prim->size(); ++k )
		{
			fprintf( fp, " %d",  (*iter_prim)[k]+1 );
		}
		fprintf( fp, "\n" );
	}
	fprintf( fp, "# %d faces\n", m_primitives.size() );

	fclose( fp );
	
	return 0;
}

} // namespace col
